/*
 * Copyright 2010 Marcin Blazejewski
 *
 * This file is part of Nofate.
 *
 * Nofate is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Nofate is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Nofate. If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "fov_point.h"
#include <list>
#include <stdlib.h>

void fp_reset(FovPoint *fp, int _x, int _y)
{
    fp->x = _x;
    fp->y = _y;
    fp->shadowX = 0;
    fp->shadowY = 0;
    fp->offsetX = 0;
    fp->offsetY = 0;
    fp->xInput = NULL;
    fp->yInput = NULL;
    fp->state = FOV_LIT;
    fp->active = TRUE;
    fp->value = 0;
}

void fp_process(FovPoint *fp, int alpha)
{
    // test if vision is obstructed
    if (0 < alpha)
    {
        fp->shadowX = abs(fp->x);
        fp->shadowY = abs(fp->y);
        fp->offsetX = fp->shadowX;
        fp->offsetY = fp->shadowY;
        fp->state = FOV_OBSTACLE;
    }
    else
    {
        bool shadowed = ((fp->shadowX != 0) || (fp->shadowY != 0));
        
        if ((fp->xInput != NULL) && ((fp->xInput->shadowX != 0) || (fp->xInput->shadowY != 0)))
        {
            // process X input
            if ((fp->xInput->offsetX > 0) && ((!shadowed) || ((fp->xInput->offsetX <= 0) && (fp->xInput->shadowY > 0))  ))
            {
                int delta = fp->xInput->shadowY;
                fp->shadowX = fp->xInput->shadowX;
                fp->shadowY = fp->xInput->shadowY;
                fp->offsetX = fp->xInput->offsetX - delta;
                fp->offsetY = fp->xInput->offsetY + delta;
            }
        }

        if ((fp->yInput != NULL) && ((fp->yInput->shadowX != 0) || (fp->yInput->shadowY != 0)))
        {
            // process Y input
            if ((fp->yInput->offsetY > 0) && ((!shadowed) || ((fp->yInput->offsetX <= 0) && (fp->yInput->shadowX > 0))  ))
            {
                int delta = fp->yInput->shadowX;
                fp->shadowX = fp->yInput->shadowX;
                fp->shadowY = fp->yInput->shadowY;
                fp->offsetX = fp->yInput->offsetX + delta;
                fp->offsetY = fp->yInput->offsetY - delta;
            }
        }
        
        shadowed = ((fp->shadowX != 0) || (fp->shadowY != 0));

        if (shadowed)
        {
            if  (  ( (fp->offsetX > 0) && (fp->offsetX <= fp->shadowX) ) 
                || ( (fp->offsetY > 0) && (fp->offsetY <= fp->shadowY)) )
            {
                fp->state = FOV_FOG;
            }
        }
    }
    
    // If all valid inputs are not lit, this point is dark and should stop expanding
    if (fp->xInput == NULL)
    {
        if (fp->yInput->state != FOV_LIT) fp->active = FALSE;
    }
    else if (fp->yInput == NULL)
    {
        if (fp->xInput->state != FOV_LIT) fp->active = FALSE;
    }
    else
    {
        if ((fp->xInput->state != FOV_LIT) && (fp->yInput->state != FOV_LIT)) fp->active = FALSE;
    }

}

/**
 * Expand fov point outward. Spawns next iteration of fov points,
 * puts them on active queue and updates fov map. If there already is
 * a fov point at the coordinates a new point would be created,
 * only update its x or y input source.
 * 
 * @param active: list of active fov points to be updated
 * @param fovmap: 2D array of fov points
 * @param ox: FOV origin x coord
 * @param oy: FOV origin y coord
 * @param maxx: fovmap X size (width)
 * @param maxy: fovmap Y size (height)
 * 
 * Internals:
 *   nx, ny - next x,y; expanded relative coords
 *   nax, nay - next abs x,y; expanded absolute coords
 */
void fp_expand(FovPoint *fp, std::list<FovPoint*> &active, FovPoint** fovmap, int ox, int oy, int maxx, int maxy)
{
    int nx, ny;
    
    if (fp->x >= 0)
    {
        nx = fp->x + 1;
        ny = fp->y;
        int nax = ox + nx;
        int nay = oy + ny;
        if (nax < maxx)
        {
            
            if ((fovmap[nax][nay].state == FOV_NONE) || (fovmap[nax][nay].state == FOV_FOG))
            {
                fp_reset(&fovmap[nax][nay], nx, ny);
                active.push_back(&fovmap[nax][nay]);
            }
            fovmap[nax][nay].xInput = fp;
        }
    }
    if (fp->y >= 0)
    {
        nx = fp->x;
        ny = fp->y + 1;
        
        int nax = ox + nx;
        int nay = oy + ny;
        if (nay < maxy)
        {
            if ((fovmap[nax][nay].state == FOV_NONE) || (fovmap[nax][nay].state == FOV_FOG))
            {
                fp_reset(&fovmap[nax][nay], nx, ny);
                active.push_back(&fovmap[nax][nay]);
            }
            fovmap[nax][nay].yInput = fp;
        }
    }
    if (fp->x <= 0)
    {
        nx = fp->x - 1;
        ny = fp->y;
        int nax = ox + nx;
        int nay = oy + ny;
        if (nax >= 0)
        {
            if ((fovmap[nax][nay].state == FOV_NONE) || (fovmap[nax][nay].state == FOV_FOG))
            {
                fp_reset(&fovmap[nax][nay], nx, ny);
                active.push_back(&fovmap[nax][nay]);
            }
            fovmap[nax][nay].xInput = fp;
        }
    }
    if (fp->y <= 0)
    {
        nx = fp->x;
        ny = fp->y - 1;
        
        int nax = ox + nx;
        int nay = oy + ny;
        if (nay >= 0)
        {
            if ((fovmap[nax][nay].state == FOV_NONE) || (fovmap[nax][nay].state == FOV_FOG))
            {
                fp_reset(&fovmap[nax][nay], nx, ny);
                active.push_back(&fovmap[nax][nay]);
            }
            fovmap[nax][nay].yInput = fp;
        }
    }
}

